#include <QtCore/QSettings>
#include <QtGui/QApplication>
#include <Box2D/Box2D.h>
#include <QyGLInputController.h>
#include <QyGLScene.h>
#include <QpwDecorationObject.h>
#include <QpwPackages.h>
#include "3rdparty/Render.h"
#include "QpwDynamicObject.h"
#include "QpwServer.h"
// #include "QpwWorld.h"

// #include <Box2D/Box2D.h>
#include "QpwPhysics.h"

void testPhysicsFunc( b2World * world ) {

    QpwWorld::Geometry geometry;
    geometry.setRadius(2);
    geometry.center = QPointF( 0, 20 );

    for ( int i = 0; i < 3; ++i ) {

        QpwWorld::Geometry subGeo;

        subGeo.center = QPointF( i * 3, 0 );
        subGeo.setRadius(1);
        geometry.subGeometry.append(subGeo);

    }

    for ( int i = 0; i < 3; ++i ) {

        QpwWorld::Geometry subGeo;

        subGeo.center = QPointF( i * 3, 3 );
        subGeo.append( QPointF( -1.5, -1.5 ) );
        subGeo.append( QPointF(  1.5, -1.5 ) );
        subGeo.append( QPointF(  1.5,  1.5 ) );
        subGeo.append( QPointF( -1.5,  1.5 ) );
        geometry.subGeometry.append(subGeo);

    }

    QpwPhysics::ObjectData::fromGeometry( world, geometry,
        QpwWorld::DecorationObject::DynamicObjectType );

}

/**************************************************************************************************/

class DebugWorldRender : public QyGL::Scene {
public:
    DebugWorldRender( b2World * world ) : p_world(world) {

        uint32 flags =
            b2DebugDraw::e_shapeBit
//             |b2DebugDraw::e_jointBit
//             |b2DebugDraw::e_aabbBit
//             |b2DebugDraw::e_pairBit
//             |b2DebugDraw::e_centerOfMassBit
        ;

        debugPhysicsRender.SetFlags(flags);
        world->SetDebugDraw(&debugPhysicsRender);

    }

    void drawGrid() {

        glBegin(GL_LINES);
        for ( qreal x = -50; x <= 50; ++ x ) {

            if ( int(x) == 0 )
                glColor3d( 0.0, 0.0, 0.0 );
            else if ( int(x) % 5 )
                glColor3d( 0.7, 0.7, 1 );
            else
                glColor3d( 0.5, 0.5, 1 );

            glVertex3d( x,-50, -0.5 );
            glVertex3d( x, 50, -0.5 );
        }

        for ( qreal y = -50; y <= 50; ++ y ) {

            if ( int(y) == 0 )
                glColor3d( 0.0, 0.0, 0.0 );
            else if ( int(y) % 5 )
                glColor3d( 0.7, 0.7, 1 );
            else
                glColor3d( 0.5, 0.5, 1 );

            glVertex3d( -50, y, 0.0 );
            glVertex3d(  50, y, 0.0 );

        }
        glEnd();


    }
    void paint() {

        drawGrid();
        p_world->DrawDebugData();

    }

private:
    DebugDraw debugPhysicsRender;
    b2World * p_world;


};

/**************************************************************************************************/

template < typename DataType >
class ContentItem : public QTreeWidgetItem {
public:
    explicit ContentItem( QTreeWidget * treeWidget, DataType & data )
        : QTreeWidgetItem(treeWidget), dataLink(data) {}

    DataType & dataLink;

};

/**************************************************************************************************/

QpwAdminTools::QpwAdminTools( QpwWorld::Processor * processor, QyGL::Scene * scene )
    : QyGL::InputController(scene),
    p_processor(processor)
{

    if (!p_processor)
        qFatal("QpwAdminTools: World processor is 0!");

    setHeaderLabels(
        QStringList()
            << "UUID"
            << "Base type"
            << "Style"
            << "Name"
            << "Description"
            << "Health"
            << "Geometry"
    );

}

void QpwAdminTools::setSubTypeDatabase( QpwSubTypeDatabase * subTypeDatabase ) {

    p_subTypeDatabase = subTypeDatabase;

    if ( p_subTypeDatabase && p_subTypeDatabase->rows.count() ) {

        clear();
        QyDbgLocation();
        QMapIterator<QUuid,QpwSubType> iter(subTypeDatabase->rows);
        ContentItem<QpwSubType> * contentItem;
        QyDbgLocation();

        while ( iter.hasNext() ) {

            iter.next();

            QpwSubType & subType = subTypeDatabase->rows[iter.key()];
            contentItem = new ContentItem<QpwSubType>( this, subType );
            contentItem->setText( 0, subType.uuid.toString() );
            contentItem->setText( 1, QpwWorld::DecorationObject::typeName(subType.baseType) );
            contentItem->setText( 2, subType.styleUuid.toString() );
            contentItem->setText( 3, subType.name );
            contentItem->setText( 4, subType.description );
            contentItem->setText( 5, QString::number(subType.health) );
            contentItem->setText( 6, subType.geometryUuid.toString() );
            contentItem->setText( 7, QString::number(subType.eventProcessorType) );
            contentItem->setText( 8, QString::number(subType.destroyProcessorType) );

        }

    }

}

QpwSubType QpwAdminTools::selectedSubType() const {

    if ( !currentItem() )
        return QpwSubType();

    ContentItem<QpwSubType> * contentItem
        = dynamic_cast<ContentItem<QpwSubType>*>( currentItem() );

    return contentItem ? contentItem->dataLink : QpwSubType();
}

void QpwAdminTools::mousePressd( const QyGL::MouseEvent & event ) {
}

void QpwAdminTools::mouseMoved( const QyGL::MouseEvent & event ) {
}

void QpwAdminTools::mouseReleased( const QyGL::MouseEvent & event ) {

    QpwSubType subType = selectedSubType();

    if ( subType.isValid() ) {

        QpwWorld::DecorationObject * object = p_processor->createObject(subType);

        switch ( object->baseType() ) {

        case QpwWorld::DecorationObject::DecorationObjectType:
            break;
        case QpwWorld::DecorationObject::StaticObjectType:
        case QpwWorld::DecorationObject::QuasiStaticObjectType:
        case QpwWorld::DecorationObject::DynamicObjectType:

            ( (QpwWorld::StaticObject*) object )->setPos( event.glPos().toPointF() );
            break;

        }

    }

}

void QpwAdminTools::on_itemActivated( QTreeWidgetItem * item, int column ) {
}

/**************************************************************************************************/

QpwServer::QpwServer( QObject * parent ) : QpwWorld::Processor(parent),
    server(this), rootDir( qApp->applicationDirPath() + "/../data" )
{

    server.setObjectName("server");
    QMetaObject::connectSlotsByName(this);
    server.setAccountManager(&accountManager);
    readConfig();
    startWorld();

    DebugWorldRender * debugScene = new DebugWorldRender( physicWorld() );
    adminTools = new QpwAdminTools( this, debugScene );
    adminTools->setSubTypeDatabase(&subTypeDatabase);

    viewWidget.setScene(debugScene);
    viewWidget.show();
    adminTools->show();
    new QyGL::BasicCamViewController(debugScene);

//     testPhysicsFunc( physicWorld() );
//     testPhysicsFunc( physicWorld() );

}

QpwServer::~QpwServer() {
}

void QpwServer::on_server_clientAuthorised( QpwNetwork::ClientRecord * clientRecord ) {

    connect( clientRecord, SIGNAL( datagramReceived(QyNet::AbstractDatagramEncoder*) ),
        SLOT( datagramReceived(QyNet::AbstractDatagramEncoder*) ) );

    qDebug( "QpwServer: New client %s", qPrintable( clientRecord->login() ) );
    QpwWorld::Player & player = players[ clientRecord->uuid() ];
    player.account = accountManager.account( clientRecord->login() );
    player.object = (QpwWorld::DynamicObject*) createObject( playerSubType, clientRecord->uuid() );
    player.movingController.setDynamicObject(player.object);
    player.movingController.setMovingSpeed(200);
    controllers.append(&player.movingController);

}

void QpwServer::on_server_clientDisconnected( QpwNetwork::ClientRecord * clientRecord ) {

    qDebug( "QpwServer: Client %s disconnected", qPrintable( clientRecord->login() ) );
    QpwWorld::Player & player = players[clientRecord->uuid()];

    controllers.removeAll(&player.movingController);
    player.movingController.setDynamicObject(0);

    if (player.object)
        removeObject(player.object);

    players.remove( clientRecord->uuid() );

}

void QpwServer::datagramReceived( QyNet::AbstractDatagramEncoder * datagram ) {

    QpwNetwork::ClientRecord * clientRecord = (QpwNetwork::ClientRecord*) sender();

    if ( !players.contains( clientRecord->uuid() ) )
        return;

    QpwWorld::Player & player = players[ clientRecord->uuid() ];

    if ( datagram->type() == QpwNetwork::VariantDatagramType ) {

        QpwNetwork::VariantDatagram * variant = (QpwNetwork::VariantDatagram *) (datagram);
        QUuid uuid;

//         qDebug( "  VariantDatagramType %d", int(variant->dataType) );

        switch (variant->dataType) {

        case QpwPackages::PlayerControlPackage:
            {

                QpwPackages::PlayerControl * playerControl = (QpwPackages::PlayerControl*) variant;

                switch ( playerControl->controlType() ) {

                case QpwPackages::PlayerControl::BeginMoving:
                    player.movingController.beginMoving();
//                     QyDbgValue( playerControl->direction() );
                    player.movingController.setMovingDirection( playerControl->direction() );
                    break;

                case QpwPackages::PlayerControl::EndMoving:
                    player.movingController.endMoving();
                    break;

                case QpwPackages::PlayerControl::ChangeDirection:
                    player.movingController.setDirection( playerControl->direction() );
                    break;

                }

            }
            break;

        }

    }

}

bool QpwServer::readConfig() {

    QSettings settings( rootDir + "/server.ini", QSettings::IniFormat );

    dataDirPath = rootDir + "/" + settings.value("server/datadir").toString();
    port = settings.value("server/port").toUInt();
    hostmask = settings.value("server/hostmask").toString();
    accountManager.checkIPType
        = QpwAccountManager::IPCheckType( settings.value("server/checktype").toUInt() );

    bool ok = true;

    ok = ok && accountManager.load( dataDirPath + "/accounts.xml" );
    ok = ok && geometryDatabase.load( dataDirPath + "/geometrys.xml" );
    ok = ok && loadData(dataDirPath);

    QUuid playerSubTypeUuid = settings.value("types/player").toString();

    if ( subTypeDatabase.rows.contains(playerSubTypeUuid) ) {

        playerSubType =  subTypeDatabase.rows[playerSubTypeUuid];

    } else {

        qWarning( "QpwServer error: Player subtype not specified or not exists [%s].",
            qPrintable( playerSubTypeUuid.toString() ) );

    }

    return ok;
}

bool QpwServer::startWorld() {

    bool ok = true;

    server.start( port, hostmask );
    start(1000/60);

    return ok;
}

void QpwServer::send( QyNet::AbstractDatagramEncoder * datagram ) {

    server.sendAll(datagram);

}

/**************************************************************************************************/

int main( int argCount, char ** args ) {

    QApplication app( argCount, args );
    QpwServer server(&app);

    return app.exec();
}
